//
//  LTDownloadViewController.m
//  Total
//
//  Created by xin on 2017/7/24.
//  Copyright © 2017年 elephants. All rights reserved.
//

#import "LTDownloadViewController.h"
#import "AppDelegate.h"
#import "LTDownloadManager.h"

@interface LTDownloadViewController () <NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDownloadDelegate>
{
    NSProgress * progress;
}
@property (weak, nonatomic) IBOutlet UIButton *downLoadButton;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (weak, nonatomic) IBOutlet UILabel *stateLabel;
@property (weak, nonatomic) IBOutlet UILabel *fileLabel;

@property (nonatomic,strong) NSURLSession * session;
@property (nonatomic,strong) NSURLSessionDownloadTask *task;
@property (nonatomic,strong) NSData *data;
@property (nonatomic,strong) NSURL *downLoadUrl;

@end

@implementation LTDownloadViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    progress = [NSProgress progressWithTotalUnitCount:10];
    [progress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))  options:NSKeyValueObservingOptionNew context:ProgressObserverContext];
    
    NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
        if (progress.completedUnitCount < progress.totalUnitCount) {
            progress.completedUnitCount += 1;
        }
    }];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/
- (IBAction)downloadFileAction:(id)sender {
    
    _downLoadUrl = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V6.0.1.dmg"];
    [self downloadFileWithUrl:_downLoadUrl];
    
}
- (IBAction)pauseAction:(id)sender {
    
    if (!_task) {
        return;
    }
    
    [_task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        _data = resumeData;
    }];
}
- (IBAction)resumeAction:(id)sender {
    
    if (!_data) {
        return;
    }else
    {
        _task = [_session downloadTaskWithResumeData:_data];
        [_task resume];
    }
}

static void *ProgressObserverContext = &ProgressObserverContext;

- (void)downloadFileWithUrl:(NSURL *)url
{
    //初始化session 设置配置为后台下载
    NSURLSessionConfiguration * config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"download"];
    config.discretionary = YES;
    
    _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
    
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    _task = [_session downloadTaskWithRequest:request];
    
    [_task resume];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if (context == ProgressObserverContext) {
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            progress = object;
            self.stateLabel.text = progress.localizedDescription;
            self.fileLabel.text = progress.localizedAdditionalDescription;
            NSLog(@"progress = %f",progress.fractionCompleted);
        }];
    }
}

#pragma mark NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    //下载过程不断回调
    NSLog(@"当前进度%f%%",totalBytesWritten * 1.0 / totalBytesExpectedToWrite * 100);
    
    dispatch_async(dispatch_get_main_queue(), ^{
        self.progressView.progress = totalBytesWritten * 1.0 / totalBytesExpectedToWrite;
    });

}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    
    //下载结束回调，注意这里得到的文件是一个临时文件，为了保存这个文件，我们需要把它copy到Document目录下
    
    NSLog(@"file--%@,path--%@",downloadTask.description,location);
    [self copyFileAtPathURL:location downloadTask:downloadTask];
    
}
#pragma mark NSURLSessionTaskDelegate
/*
 一个HTTP请求试图执行重定向到另一个URL。你必须调用完成例行的允许重定向,允许修改请求重定向,或通过nil completionHandler导致身体的重定向响应这个请求的负载。默认的是遵循重定向。任务在后台会话,重定向将始终遵循这个方法将不会被调用。
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
        newRequest:(NSURLRequest *)request
 completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
{
    NSLog(@"%s=(%d)",__func__,__LINE__);
}

/*
 任务已收到一个请求特定的身份验证的挑战。如果没有实现,这个委托会话特定身份验证的挑战*不*会打电话的行为将使用默认处理的一样吗处置。
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
    NSLog(@"%s=(%d)",__func__,__LINE__);
}

/* 如果一个任务需要发送一个新的,未开封的body流。这可能是必要时要求身份验证失败包括body流。
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
 needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler
{
    NSLog(@"%s=(%d)",__func__,__LINE__);
}

/* 定期发送通知委托的上传进度。这信息也可以作为属性的任务。
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    NSLog(@"%s=(%d)",__func__,__LINE__);
}

/*
 * Sent when complete statistics information has been collected for the task.
 发送完成后收集统计信息的任务。
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
{
    //API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)
}

/* 作为最后一条消息发送相关的特定任务。错误可能为空,这意味着没有错误发生,这个任务就完成了。
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error;
{
    NSLog(@"%s=(%d,%@)",__func__,__LINE__,error.description);
}

#pragma mark NSURLSessionDelegate

/* If implemented, when a connection level authentication challenge
 * has occurred, this delegate will be given the opportunity to
 * provide authentication credentials to the underlying
 * connection. Some types of authentication will apply to more than
 * one request on a given connection to a server (SSL Server Trust
 * challenges).  If this delegate message is not implemented, the
 * behavior will be to use the default handling, which may involve user
 * interaction.
 如果实现,当连接级别的身份验证的挑战发生,这个委托将有机会吗提供潜在的身份验证凭证连接。
 将适用于超过某些类型的身份验证一个请求在一个给定的连接到服务器(SSL服务器的信任挑战)。
 如果不实现,这个委托信息行为将会使用默认的处理,这可能涉及用户交互。
 */

//- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
// completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
//{
//    NSLog(@"%s=(%d)",__func__,__LINE__);
//}

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    NSLog(@"%s=(%d)",__func__,__LINE__);
    /* If an application has received an
     * -application:handleEventsForBackgroundURLSession:completionHandler:
     * message, the session delegate will receive this message to indicate
     * that all messages previously enqueued for this session have been
     * delivered.  At this time it is safe to invoke the previously stored
     * completion handler, or to begin any internal updates that will
     * result in invoking the completion handler.
     如果一个应用程序收到了一条handleEventsForBackgroundURLSession:completionHandler:的消息,
     会话委托将收到此消息指示所有消息之前进行入队这个会话交付。
     这个时候是安全调用先前存储完成处理器,或开始任何内部更新导致调用完成处理器。
     */
    
    AppDelegate * delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    if(delegate.backgroundSessionCompletionHandler){
        void(^completionHander)() = delegate.backgroundSessionCompletionHandler;
        delegate.backgroundSessionCompletionHandler = nil;
        completionHander();
    }
    NSLog(@"all tasks are finished");
}

- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
{
    NSLog(@"ERR: URL session did become invalid with error: %@ (%@, %d)", error, [NSString stringWithUTF8String:__FILE__].lastPathComponent, __LINE__);
    
    /* The last message a session receives.  A session will only become
     * invalid because of a systemic error or when it has been
     * explicitly invalidated, in which case the error parameter will be nil.
     * 最后一条消息会话接收。一个会话只会变得无效,因为系统性错误或已明确失效时,在这种情况下,错误参数将是零。
     */
    
}


#pragma mark copy file
- (void)copyFileAtPathURL:(NSURL *)pathURL downloadTask:(NSURLSessionDownloadTask *)downloadTask
{
    NSFileManager * fileManager = [NSFileManager defaultManager];
    
    NSError * error;
    
    NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    
    NSString * documentDirectory = [paths objectAtIndex:0];
    
    NSString * toPath = [documentDirectory stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    
    NSLog(@"toPath=%@",toPath);
    //必须使用 fileURLWithPath: 不能使用url
    if ((![fileManager fileExistsAtPath:toPath])) {
        [fileManager moveItemAtURL:pathURL toURL:[NSURL fileURLWithPath:toPath] error:&error];
        
        if (error) {
            NSLog(@"copy error--%@",error.description);
        }
    }
}

@end
